#include "umlproject.h"
#include "uml/umldiritem.h"
#include "gui/mainwindow/mainwindow.h"
#include "uml/umlgraphicsitem.h"
#include "gui/dock/projectdock/projectmodel.h"

UmlProject::UmlProject(QObject *parent) : QGraphicsScene(parent)
{
    this->_id = QUuid::createUuid().toString();
    this->_type = UmlProject::GenericProject;
    this->_name = QObject::tr("New Project");
    this->_umlItemRoot = new UmlDirItem();
    this->_model = new ProjectModel();
    this->_model->setProjectData(this->_umlItemRoot);
    this->_dataTypes = new DatatypeDictionary();
    this->_draggedRelation = 0;
    this->_isPressed = false;

    QPrinter printer;

    QRectF rect = printer.paperRect(QPrinter::Point);

    this->setSceneRect(rect);

    QObject::connect(this, SIGNAL(selectionChanged()), this, SLOT(selectionHasChanged()));
    QObject::connect(this, SIGNAL(changed(QList<QRectF>)), this, SLOT(sceneHasChanged()));
}

UmlProject::~UmlProject()
{
    qDebug() << "deleting umlProject:" << this->_name;

    delete this->_umlItemRoot;
    this->_umlItemRoot = 0;

    /*if (this->_dataTypes != 0) {
        delete this->_dataTypes;
        this->_dataTypes = 0;
    }*/

    this->clear();
}

QString UmlProject::id()
{
    return this->_id;
}

void UmlProject::setId(QString id)
{
    this->_id = id;
}

QString UmlProject::name()
{
    return this->_name;
}

void UmlProject::setName(QString name)
{
    this->_name = name;
}

QString UmlProject::path() {
    return this->_path;
}

void UmlProject::setPath(QString path) {
    this->_path = path;
}

bool UmlProject::hasChanged()
{
    return this->_hasChanged;
}

void UmlProject::setHasChanged(bool hasChanged)
{
    this->_hasChanged = hasChanged;
}

UmlProject::ProjectType UmlProject::type()
{
    return this->_type;
}

void UmlProject::setType(UmlProject::ProjectType type)
{
    this->_type = type;
}


UmlDirItem* UmlProject::umlItemRoot()
{
    return this->_umlItemRoot;
}

void UmlProject::setUmlItemRoot(UmlDirItem *root)
{
    this->_umlItemRoot = root;
    this->_model->setProjectData(this->_umlItemRoot);
}

DatatypeDictionary* UmlProject::dataTypes()
{
    return this->_dataTypes;
}

void UmlProject::setDataTypes(DatatypeDictionary *dataTypes)
{
    /*if (this->_dataTypes != 0) {
        delete this->_dataTypes;
        this->_dataTypes = 0;
    }*/
    this->_dataTypes = dataTypes;
}

void UmlProject::setMainWindow(MainWindow *mainWindow)
{
    this->_mainWindow = mainWindow;

    this->_mainWindow->_project = this;

    // Project Browser
    this->_mainWindow->_projectDock = new ProjectDock(this->_mainWindow);
    //this->_mainWindow->_projectDock->setProjectData(this);
    this->_mainWindow->_projectDock->setModel(this->_model);
    this->_mainWindow->addDockWidget(Qt::RightDockWidgetArea, this->_mainWindow->_projectDock);
    QObject::connect(this->_mainWindow->_projectDock, SIGNAL(drawPackage(UmlDirItem*)), this, SLOT(drawPackage(UmlDirItem*)));
    //QObject::connect(this->_mainWindow->_projectDock, SIGNAL(visibilityChanged(bool)), this, SLOT(projectBrowserClosed()));

    this->showProjectBrowser();

    // Toolbox
    this->_mainWindow->_toolDock = new ClassDiagrammDock(this->_mainWindow);
    this->_mainWindow->addDockWidget(Qt::RightDockWidgetArea, this->_mainWindow->_toolDock);
    //QObject::connect(this->_mainWindow->_toolDock, SIGNAL(visibilityChanged(bool)), this, SLOT(toolbarClosed()));

    this->showToolbar();

    // setting scene
    this->_mainWindow->setProject(this);

    this->drawPackage(this->_umlItemRoot);
}

void UmlProject::addUmlitem(UmlDirItem *dir, UmlGraphicsItem *item)
{
    // setting parent
    item->setParent(this);

    // adding item to scene
    this->addItem(item);

    // adding item to model
    this->_model->insertItem(dir, item);

    // mark project as "changed"
    this->setHasChanged(true);
}

void UmlProject::removeUmlItem(UmlGraphicsItem *item)
{
    // removing item from scene
    this->removeItem(item);

    // removing item from model
    this->_model->removeItem(item);

    // removing datatype
    this->_dataTypes->removeByLabel(item->name());

    // deleting item
    delete item;
}

DraggedRelation* UmlProject::draggedRelation()
{
    return this->_draggedRelation;
}

bool UmlProject::hasDraggedRelation()
{
    return this->_draggedRelation;
}

void UmlProject::showGuides(bool showGuides)
{
    this->_leftBorder->setVisible(showGuides);
    this->_rightBorder->setVisible(showGuides);
    this->_topBorder->setVisible(showGuides);
    this->_bottomBorder->setVisible(showGuides);
}

UmlGraphicsItem* UmlProject::itemUnderMouse(QPointF pos)
{
    QList<QGraphicsItem*> items = this->items(pos);
    UmlGraphicsItem *umlItem = 0;

    foreach (QGraphicsItem *item, items) {
        umlItem = dynamic_cast<UmlGraphicsItem*>(item);

        if (umlItem) {
            return umlItem;
        }
    }

    return 0;
}

QRectF UmlProject::selectionRect()
{
    QRectF selectionRect;

    QList<QGraphicsItem*> list = this->selectedItems();
    foreach (QGraphicsItem *item, list) {
        selectionRect = selectionRect.united(item->mapRectToScene(item->boundingRect()));

        UmlRelationalItem *relItem = dynamic_cast<UmlRelationalItem*>(item);

        if (relItem) {
            foreach (UmlRelation *relation, relItem->relations()) {
                selectionRect = selectionRect.united(relation->mapRectToScene(relation->boundingRect()));
            }
        }

    }

    return selectionRect;
}

void UmlProject::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton) {
        this->_isPressed = true;
    }

    QGraphicsScene::mousePressEvent(event);
}

void UmlProject::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    this->_isPressed = false;

    if (this->_draggedRelation) {
        delete this->_draggedRelation;
        this->_draggedRelation = 0;
    }

    QGraphicsScene::mouseReleaseEvent(event);
}

void UmlProject::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if (this->_isPressed && this->_draggedRelation) {
        this->_draggedRelation->setMousePos(event->scenePos());

        this->itemUnderMouse(event->scenePos());

    }

    QGraphicsScene::mouseMoveEvent(event);
}

void UmlProject::initToolbar(QToolBar *toolbar)
{
    Q_UNUSED(toolbar);
}

QDockWidget* UmlProject::itemDock()
{
    return new QDockWidget();
}

ProjectModel* UmlProject::model()
{
    return this->_model;
}

void UmlProject::drawPackage(UmlDirItem *dir)
{
    // no change
    if (this->_currentDir == dir) {
        return;
    }

    // empty project
    if (this->_umlItemRoot == 0) {
        return;
    }

    // removing all current items
    QListIterator<QGraphicsItem*> iterDel(this->items());
    while (iterDel.hasNext()) {
        this->removeItem(iterDel.next());
    }

    // drawing printer guides
    this->drawGuides();

    // adding new items
    QListIterator<UmlGraphicsItem*> iterAdd(dir->items());
    while (iterAdd.hasNext()) {
        this->addItem(iterAdd.next());
    }

    this->_currentDir = dir;
}

void UmlProject::startRelationDrag(DraggedRelation *draggedRelation)
{
    if (!this->_draggedRelation) {
        this->_draggedRelation = draggedRelation;
        this->addItem(this->_draggedRelation);
    }
}

void UmlProject::selectionHasChanged()
{
    this->_mainWindow->enableSelectionExport(this->selectedItems().isEmpty());
}

void UmlProject::showProjectBrowser()
{
    if (!this->_mainWindow->_projectDock) {
        // Project Browser
        this->_mainWindow->_projectDock = new ProjectDock(this->_mainWindow);
        this->_mainWindow->_projectDock->setModel(this->_model);
        this->_mainWindow->addDockWidget(Qt::RightDockWidgetArea, this->_mainWindow->_projectDock);
        QObject::connect(this->_mainWindow->_projectDock, SIGNAL(drawPackage(UmlDirItem*)), this, SLOT(drawPackage(UmlDirItem*)));
        QObject::connect(this->_mainWindow->_projectDock, SIGNAL(visibilityChanged(bool)), this, SLOT(projectBrowserClosed()));
    }
}

void UmlProject::showToolbar()
{
    if (!this->_mainWindow->_toolDock) {
        this->_mainWindow->_toolDock = new ClassDiagrammDock(this->_mainWindow);
        this->_mainWindow->addDockWidget(Qt::RightDockWidgetArea, this->_mainWindow->_toolDock);
        QObject::connect(this->_mainWindow->_toolDock, SIGNAL(visibilityChanged(bool)), this, SLOT(toolbarClosed()));
    }
}

void UmlProject::sceneHasChanged()
{
    this->setHasChanged(true);
}

void UmlProject::projectBrowserClosed()
{
    qDebug() << "projectDock destroyed";
    this->_mainWindow->_projectDock = 0;
}

void UmlProject::toolbarClosed()
{
     qDebug() << "toolDock destroyed";
    this->_mainWindow->_toolDock = 0;
}

void UmlProject::drawGuides()
{
    QPrinter printer;

    QRectF rect = printer.paperRect(QPrinter::Point);

    QPen pen;
    pen.setColor(Qt::gray);

    this->_topBorder = this->addLine(QLineF(rect.topLeft(), rect.topRight()), pen);
    this->_topBorder->setZValue(std::numeric_limits<int>::min());

    this->_bottomBorder = this->addLine(QLineF(rect.bottomLeft(), rect.bottomRight()), pen);
    this->_bottomBorder->setZValue(std::numeric_limits<int>::min());

    this->_leftBorder = this->addLine(QLineF(rect.topLeft(), rect.bottomLeft()), pen);
    this->_leftBorder->setZValue(std::numeric_limits<int>::min());

    this->_rightBorder = this->addLine(QLineF(rect.topRight(), rect.bottomRight()), pen);
    this->_rightBorder->setZValue(std::numeric_limits<int>::min());
}
